package edu.nf.supermarket.service.impl;

import edu.nf.supermarket.entity.Commodity;
import edu.nf.supermarket.entity.User;
import edu.nf.supermarket.mapper.CommodityMapper;
import edu.nf.supermarket.service.RecommendService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author yjj
 * @date 2025-05-23
 * 推荐服务实现类
 */
@Service("recommendService")
public class RecommendServiceImpl implements RecommendService {
    @Autowired
    private CommodityMapper commodityMapper;

    /**
     * 通过推荐算法获取商品信息方法
     * @param userId
     * @return
     */
    @Override
    public List<Commodity> recommend(int userId) {
        //拿到该用户的所有购买记录
        List<Commodity> commodityByUserId = commodityMapper.findCommodityByUserId(userId);
        //判断用户是否有购买记录， 没有则无法推荐
        if (commodityByUserId.isEmpty()) {
            return Collections.emptyList();
        }
        //获取所有用户的购买记录
        Map<Integer, List<Commodity>> allUserPurchases = findAllUserPurchases();
        //计算并存储客户相似度
        Map<Integer, Double> similarity = new HashMap<>();
        for (Integer userNo : allUserPurchases.keySet()) {
            if (userNo == userId) continue;
            double v = cosineSimilarity(commodityByUserId, allUserPurchases.get(userNo));
            similarity.put(userNo, v);
        }

        //获取相似度最高的5个客户
        List<Integer> topKUsers = similarity.entrySet()
                .stream()
                //根据相似度值进行排序，reversed()倒序
                .sorted(Map.Entry.<Integer, Double>comparingByValue().reversed())
                .limit(5)
                //获取用户id
                .map(Map.Entry::getKey)
                //将用户id放入list集合中
                .collect(Collectors.toList());
        Set<Commodity> recommendCommodities = new HashSet<>();
        //筛选出登录客户和相似客户购买过的商品
        for (Integer userNo : topKUsers) {
            List<Commodity> commodities = allUserPurchases.get(userNo);
            for (Commodity commodity : commodities) {
                boolean flag = commodityByUserId.stream().anyMatch(c -> Objects.equals(c.getCommodityId(), commodity.getCommodityId()));
                if (!flag) {
                    recommendCommodities.add(commodity);
                }

            }
        }
        List<Commodity> recommendCommodityList = new ArrayList<>(recommendCommodities);
        recommendCommodityList = recommendCommodityList.stream()
                .map(c -> commodityMapper.findCommodityById(c.getCommodityId()))
                .collect(Collectors.toList());
        return recommendCommodityList;

    }
    //获取所有用户的购买记录
    private Map<Integer, List<Commodity>> findAllUserPurchases() {
        List<User> allUserId = commodityMapper.findAllUserId();
        Map<Integer, List<Commodity>> allUserPurchases = new HashMap<>();
        for (User user : allUserId) {
            List<Commodity> commodities = commodityMapper.findUserProductsByUserIds(user.getUserId());
            allUserPurchases.put(user.getUserId(), commodities);
        }
        return allUserPurchases;
    }

    /**
     * 计算客户相似度
     *
     * @param listA 登录客户
     * @param listB 对比客户
     * @return 相似度
     */
    private double cosineSimilarity(List<Commodity> listA, List<Commodity> listB) {
        //获取用户购买的商品id， 并收集到set集合中
        Set<Integer> collectA = listA.stream().map(Commodity::getCommodityId).collect(Collectors.toSet());
        Set<Integer> collectB = listB.stream().map(Commodity::getCommodityId).collect(Collectors.toSet());
        Set<Integer> intersection = new HashSet<>(collectA);
        //求交集 即用户购买相同商品
        intersection.retainAll(collectB);

        int size = intersection.size();
        //算平方根（开根号） ，将值缩小在零和一之间却靠近一值越大， 避免相似偏向购买量大的一方
        double sqrtA = Math.sqrt(collectA.size());
        double sqrtB = Math.sqrt(collectB.size());
        if (sqrtA == 0 || sqrtB == 0) {
            return 0.0;
        }
        return size / (sqrtA * sqrtB);
    }
}
